<?php
// +----------------------------------------------------------------------
// | UCToo [ Universal Convergence Technology ]
// +----------------------------------------------------------------------
// | Copyright (c) 2014-2021 https://www.uctoo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: UCToo <contact@uctoo.com>
// +----------------------------------------------------------------------
declare(strict_types=1);

namespace uctoo\library;

use Peast\Formatter\Compact;
use Peast\Formatter\PrettyPrint;
use Peast\Peast;
use Peast\Renderer;

class JsParser
{
    /**
     * 根据语法树合并javascript源代码
     * Merge javascript source code by AST
     * @param $string1
     * @param $string2
     * @return string the resulting javascript source code
     */
    public static function js_merge($string1,$string2)
    {
        $options = array(
            "sourceType" => Peast::SOURCE_TYPE_MODULE,
            "comments" => true
        );
        $templateAst = Peast::latest($string1, $options)->parse();
        $mergeAst = Peast::latest($string2, $options)->parse();
        //Create the renderer
        $renderer = new Renderer;
        //Associate the formatter
        $renderer->setFormatter(new Compact);
        $compactString1 = $renderer->render($templateAst);   //压缩后可能对简化算法有用
        $compactString2 = $renderer->render($mergeAst);
        $templateAst = Peast::latest($compactString1, $options)->parse();
        $mergeAst = Peast::latest($compactString2, $options)->parse();

        //解析import 代码段  todo:应该做相同import内容的去重
        $templateImportQuery = $templateAst->query("ImportDeclaration");
        $tempImportCount = $templateImportQuery->count();
        $templateImportFirst = $templateImportQuery->get(0);
        $loctemplateImport = $templateImportFirst->getLocation();
        $loctemplateImportStart = $loctemplateImport->start;
        $templateImportLast = $templateImportQuery->get($tempImportCount-1);
        $loctemplateImport = $templateImportLast->getLocation();
        $loctemplateImportEnd = $loctemplateImport->end;

        $mergeImportQuery = $mergeAst->query("ImportDeclaration");
        $mergeImportCount = $mergeImportQuery->count();

        $templateImportcode = '';
        for ($x=0; $x<$tempImportCount; $x++) {
            $templateImport = $templateImportQuery->get($x);
            $templateImportcode = $renderer->render($templateImport).';'.$templateImportcode;
        }
        $mergeImportcode = '';
        for ($x=0; $x<$mergeImportCount; $x++) {
            $mergeImport = $mergeImportQuery->get($x);
            $mergeImportcode = $renderer->render($mergeImport).';'.$mergeImportcode;
        }
        $newImport = $templateImportcode.$mergeImportcode;

        //解析data() { return {  代码段
        $templatedataReturnQuery = $templateAst->query("Identifier[name='data'] + FunctionExpression BlockStatement ReturnStatement > ObjectExpression");
        $templatedataReturn = $templatedataReturnQuery->get(0);
        $loctemplatedataReturn = $templatedataReturn->getLocation();
        $loctemplatedataReturnStart = $loctemplatedataReturn->start;
        $loctemplatedataReturnEnd = $loctemplatedataReturn->end;

        $mergedataReturnQuery = $mergeAst->query("Identifier[name='data'] + FunctionExpression BlockStatement ReturnStatement > ObjectExpression");
        $mergedataReturn = $mergedataReturnQuery->get(0);

        $templatedataReturncode = $renderer->render($templatedataReturn);
        $mergedataReturncode = $renderer->render($mergedataReturn);
        $newdataReturn = substr($templatedataReturncode, 0, -1) . ',' . substr($mergedataReturncode, 1);       //todo:简单字符串合并，因此有相同变量声明时没有做去重

        //解析mounted() {  代码段
        $templateMountedQuery = $templateAst->query("Identifier[name='mounted'] + FunctionExpression BlockStatement");
        $templateMountedNode = $templateMountedQuery->get(0);
        $loctemplateMounted = $templateMountedNode->getLocation();
        $loctemplateMountedStart = $loctemplateMounted->start;
        $loctemplateMountedEnd = $loctemplateMounted->end;

        $mergeMountedQuery = $mergeAst->query("Identifier[name='mounted'] + FunctionExpression BlockStatement");
        $mergeMountedNode = $mergeMountedQuery->get(0);

        $templateMountedcode = $renderer->render($templateMountedNode);
        $mergeMountedcode = $renderer->render($mergeMountedNode);
        $newMounted = "{" . $templateMountedcode . $mergeMountedcode . "}";    //不知道为什么把花括号吞了

        //解析methods: { init() { 代码段
        $templateMethodsInitQuery = $templateAst->query("Identifier[name='methods'] + ObjectExpression Property Identifier[name='init'] + FunctionExpression BlockStatement");
        $templateMethodsInitNode = $templateMethodsInitQuery->get(0);
        $loctemplateMethodsInit = $templateMethodsInitNode->getLocation();
        $loctemplateMethodsInitStart = $loctemplateMethodsInit->start;
        $loctemplateMethodsInitEnd = $loctemplateMethodsInit->end;

        $mergeMethodsInitQuery = $mergeAst->query("Identifier[name='methods'] + ObjectExpression Property Identifier[name='init'] + FunctionExpression BlockStatement");
        $mergeMethodsInitNode = $mergeMethodsInitQuery->get(0);

        $templateMethodsInitcode = $renderer->render($templateMethodsInitNode);
        $mergeMethodsInitcode = $renderer->render($mergeMethodsInitNode);
        $newMethodsInit = "init(){" . $templateMethodsInitcode . $mergeMethodsInitcode . "}";  //也许这个部分语法树就是吞括号的

        //解析methods: { 去除 init() { 部分的代码段
        $templateMethodsQuery = $templateAst->query("Identifier[name='methods'] + ObjectExpression");
        $templateMethodsNode = $templateMethodsQuery->get(0);
        $loctemplateMethods = $templateMethodsNode->getLocation();
        $loctemplateMethodsStart = $loctemplateMethods->start;
        $loctemplateMethodsEnd = $loctemplateMethods->end;

        $templateInitQuery = $templateAst->query("Identifier[name='methods'] + ObjectExpression Property");
        $templateInitNode = $templateInitQuery->get(0);

        $mergeMethodsQuery = $mergeAst->query("Identifier[name='methods'] + ObjectExpression");
        $mergeMethodsNode = $mergeMethodsQuery->get(0);
        $mergeInitQuery = $mergeAst->query("Identifier[name='methods'] + ObjectExpression Property");
        $mergeInitNode = $mergeInitQuery->get(0);

        $templateMethodscode = $renderer->render($templateMethodsNode);
        $templateInitcode = $renderer->render($templateInitNode);
        $mergeMethodscode = $renderer->render($mergeMethodsNode);
        $mergeInitcode = $renderer->render($mergeInitNode);
        $templateMethodscode = str_replace($templateInitcode, $newMethodsInit, $templateMethodscode);
        $mergeMethodscode = str_replace($mergeInitcode, " ", $mergeMethodscode);
        $newMethods = substr($templateMethodscode, 0, -1) . substr($mergeMethodscode, 1);

        //解析computed: { 部分的代码段
        $templateComputedQuery = $templateAst->query("Identifier[name='computed'] + ObjectExpression");
        $templateComputedNode = $templateComputedQuery->get(0);
            $loctemplateComputed = $templateComputedNode->getLocation();
           $loctemplateComputedStart = $loctemplateComputed->start;
           $loctemplateComputedEnd = $loctemplateComputed->end;

           $mergeComputedQuery = $mergeAst->query("Identifier[name='computed'] + ObjectExpression");
           $mergeComputedNode = $mergeComputedQuery->get(0);
           $templateComputedcode = $renderer->render($templateComputedNode);
           $mergeComputedcode = $renderer->render($mergeComputedNode);
           if(trim($templateComputedcode,'{}')){     //同一模板多个控件时合并代码
               $newComputed = substr($templateComputedcode, 0, -1).',' . substr($mergeComputedcode, 1);
           }else{
               $newComputed = substr($templateComputedcode, 0, -1) . substr($mergeComputedcode, 1);
           }

        //解析watch: {} 部分的代码段
        $templateWatchQuery = $templateAst->query("Identifier[name='watch'] + ObjectExpression");
        $templateWatchNode = $templateWatchQuery->get(0);
        $loctemplateWatch = $templateWatchNode->getLocation();
        $loctemplateWatchStart = $loctemplateWatch->start;
        $loctemplateWatchEnd = $loctemplateWatch->end;

        $mergeWatchQuery = $mergeAst->query("Identifier[name='watch'] + ObjectExpression");
        $mergeWatchNode = $mergeWatchQuery->get(0);
        $templateWatchcode = $renderer->render($templateWatchNode);
        $mergeWatchcode = $renderer->render($mergeWatchNode);
        if(trim($templateWatchcode,'{}')){     //同一模板多个控件时合并代码
            $newWatch = substr($templateWatchcode, 0, -1).',' . substr($mergeWatchcode, 1);
        }else{
            $newWatch = substr($templateWatchcode, 0, -1) . substr($mergeWatchcode, 1);
        }

        $temptoken = Peast::latest($compactString1, $options)->tokenize();

        $newcode = '';                       //没看懂Peast怎么添加node，只能这么拼接代码了
        $dataReturnToken = false;            //是否替换标识符
        $mountedToken = false;
        $methodsToken = false;
        $importToken = false;
        $computedToken = false;
        $watchToken = false;
        foreach ($temptoken as $token) {
            //开始替换代码段
            if ($token->getLocation()->getStart() == $loctemplateImportStart) {
                $importToken = true;
                $newcode = $newcode . $newImport;
            }
            if ($token->getLocation()->getStart() == $loctemplatedataReturnStart) {
                $dataReturnToken = true;
                $newcode = $newcode . $newdataReturn;
            }
            if ($token->getLocation()->getStart() == $loctemplateMountedStart) {
                $mountedToken = true;
                $newcode = $newcode . $newMounted;
            }
           if ($token->getLocation()->getStart() == $loctemplateComputedStart) {
                $computedToken = true;
                $newcode = $newcode . $newComputed;
            }
            if ($token->getLocation()->getStart() == $loctemplateWatchStart) {
                $watchToken = true;
                $newcode = $newcode . $newWatch;
            }
            if ($token->getLocation()->getStart() == $loctemplateMethodsStart) {
                $methodsToken = true;
                $newcode = $newcode . $newMethods;
            }

            //无需替换代码段
            if (!$importToken && !$dataReturnToken && !$mountedToken && !$methodsToken && !$computedToken && !$watchToken) {
                $newcode = $newcode . $token->getValue() . ' ';
            }

            //结束替换代码段
            if ($token->getLocation()->getEnd() == $loctemplateImportEnd) {
                $importToken = false;
            }
            if ($token->getLocation()->getEnd() == $loctemplatedataReturnEnd) {
                $dataReturnToken = false;
            }
            if ($token->getLocation()->getEnd() == $loctemplateMountedEnd) {
                $mountedToken = false;
            }
            if ($token->getLocation()->getEnd() == $loctemplateComputedEnd) {
                $computedToken = false;
            }
            if ($token->getLocation()->getEnd() == $loctemplateWatchEnd) {
                $watchToken = false;
            }
            if ($token->getLocation()->getEnd() == $loctemplateMethodsEnd) {
                $methodsToken = false;
            }
        }
        $newcodeparse = Peast::latest($newcode, $options)->parse();  //重新代码格式化
        $renderer->setFormatter(new PrettyPrint);
        $newcode = $renderer->render($newcodeparse);

       return $newcode;
    }
}